#ifndef FSXSIMCONNECT_H
#define FSXSIMCONNECT_H

#include <QString>
#include <QList>
#include <QMap>
#include <QThread>
#include <QMutex>
#include <windows.h>
#include <WinDef.h>
#include <strsafe.h>
#include <QTimer>
#include "fsxdatadefinitionshelper.h"
#include "fsxsimconnectqtfier.h"
#include "aircraftmap.h"

namespace FsxSimConnect {

//! Settings for SimConnect, subset of FsxWsSettings
/*! \sa FsxWsSettings
    \author KWB */
struct FSXSimConnectSettings {
    //! Default constructor.
    FSXSimConnectSettings();
    //! Full constructor.
    /*! \param radiusMeter
        \param period
        \param callbackStatistics
        \param maximumWaypoints
        \param waypointsTimeFrameMs
        \param kmlModelUrl
        \param kmlUpdatePeriodS */
    FSXSimConnectSettings(DWORD radiusMeter, SIMCONNECT_PERIOD period, bool callbackStatistics, int maximumWaypoints, int waypointsTimeFrameMs,
                          QString kmlModelUrl, int kmlUpdatePeriodS);
    DWORD fsxAircraftRequestRadiusMeter; /*!< other aircrafts fetched within radius [m] */
    SIMCONNECT_PERIOD fsxSimObjectRequestPeriod; /*!< period how often SimConnect requests data */
    bool fsxCallbackStatistics; /*!< automatically dump callback stats for FSX */
    QString kmlModelUrl; /*!< url of KML model */
    int kmlNetworkLinkUpdatePeriodS;  /*!< period of updates when KML is NetworkLink based */
    int modelMaximumWaypointsNumber;  /*!< maximum number of waypoints used with the model */
    qint64 modelWaypointsTimeFrameMs; /*!< waypoint time frame used with the model */
};

//! Connecting to FSX and handling data transfer
class FSXSimConnect : public QObject
{
    Q_OBJECT

public:
    //! Constructor.
    /*! \param settings
        \param parent Qt parent object */
    FSXSimConnect(FSXSimConnectSettings settings = FSXSimConnectSettings(), QObject *parent = 0);
    //! Destructor
    ~FSXSimConnect();
    //! Close the connection to FSX.
    /*! \sa FSXSimConnect::killSimConnect() */
    void closeSimConnect();
    //! Open the connection to FSX.
    /*! \return opened */
    bool openSimConnect();
    //! Start fetching the data
    void start();
    //! Stop fetching the data
    void stop();
    //! Log the statistics information.
    void logStatistics();
    //! Fetching data?
    /*! \return running */
    bool isRunning() { return _runMessageLoop; }
private:
    mutable QMutex _mutex; /*!< Mutex for thread safety */
    QTimer *_dispatchTimer; /*!< Timer trigger fetching the FSX data */
    QMap<QString, int> _callbackStatistics; /*!< counting the callback calls */
    qint64 _lastStatisticsLog; /*!< time for statistics log, used for timeout */
    HANDLE* _pHandleSimConnect; /*!< SimConnect handle */
    bool _runMessageLoop; /*!< Message loop running? */
    bool _initDataDefinitions; /*!< Data definitions have been initialized */
    bool _initSystemEvents; /*!< System events have been initialized */
    FSXSimConnectSettings _settings; /*!< "My settings" */
    AircraftMap _aircrafts; /*!< Aircrafts retrieved with this conenction */
    QMap<SIMCONNECT_OBJECT_ID, SIMCONNECT_DATA_REQUEST_ID> _objectIdToRequest; /*!< Id to request id */
    //! Request data on given SimObject.
    /*! \param SIMCONNECT_OBJECT_ID id
        \return int number of failures, >0 means at least one problem */
    int  requestDataOnSimObject(SIMCONNECT_OBJECT_ID objectId = SIMCONNECT_OBJECT_ID_USER);
    //! Request aircraft data.
    /*! \return int number of failures, >0 means at least one problem */
    int  requestAircraftData();
    //! Unrequest data on given SimObject.
    /*! \param SIMCONNECT_OBJECT_ID id */
    void unrequestDataOnSimObject(SIMCONNECT_OBJECT_ID objectId = SIMCONNECT_OBJECT_ID_USER);
    //! Kill the FSX connection, e.g. if FSX quits
    void killSimConnect();
    //! Init the data definitions.
    /*! \return int number of failures, >0 means at least one problem */
    int initDataDefinitions();
    //! Init the system events.
    /*! \return int number of failures, >0 means at least one problem */
    int initSystemEvents();
    //! Log a message
    /*! \param msg log message
        \param msgType */
    void log(const QString &msg, QtMsgType msgType = QtDebugMsg);
    /*! Increase statistics count */
    void increaseStatisticsCount(const QString &key);
    //! Central callback method, static Callback from FSX.
    /*! \param SIMCONNECT_RECV* Pointer to a data buffer, to be treated initially as a SIMCONNECT_RECV structure. If you are going to make a copy of the data buffer (which is maintained by the SimConnect client library) make sure that the defined buffer is large enough (the size of the returned data structure is one member of the SIMCONNECT_RECV structure.
        \param DWORD cbData The size of the data buffer, in bytes. -> DWORD cbData
        \param void  pContext Contains the pointer specified by the client in the SimConnect_CallDispatch function call. */
    static void CALLBACK simConnectDispatch(SIMCONNECT_RECV* pData, DWORD cbData, void* pContext);
    //! Event ids.
    enum EVENT_ID {EVENT_ADDED_AIRCRAFT, EVENT_REMOVED_AIRCRAFT};

    // enum REQUEST_ID {REQUEST_NOTUSEDYET=FSXDataDefinitionsHelper::RequestDefinitionEndMarker};
    // enum DATA_DEFINITION_ID {DEF_NOTUSEDYET=FSXDataDefinitionsHelper::DataDefinitionEndMarker};

signals:
    //! Log a message.
    /*! \param msg
        \param msgType severity */
    void signalLogMessage(const QString &msg, QtMsgType msgType); // Do not write FSXSimConnect::signalLogMessage, MOC will fail
    //! FSX quits.
    void signalQuit();
    //! Message loop has been stopped.
    void signalMessageLoopStopped();
public slots:
    //! Provide aircrafts if requested
    /*! \return Aircrafts */
    QList<Aircraft> slotAircrafts();
    //! Provide JSON data of aircrafts (if requested)
    /*! \param parameter JSON parameters
        \return JSON data */
    QString  slotAircraftsJsonArray(const JsonParameter &parameter);
    //! Provide HTML data of aircrafts (if requested)
    /*! \return HTML data */
    QString  slotAircraftsHtmlTable();
    //! Provide KML waypoints data of aircraft (if requested)
    /*! \param parameter KML parameters
        \return KML waypoints */
    QString  slotAircraftKmlWaypoints(const KmlParameter &parameter);
    //! Provide aircraft by id (if requested)
    /*! \param id aircraft id
        \return Aircraft */
    Aircraft slotFindAircraftById(const SIMCONNECT_OBJECT_ID id);
    //! Update settings
    /*! \param settings */
    void slotSetSettings(FSXSimConnectSettings settings);
private slots:
    //! Fetch data from FSX
    void slotDispatchSimConnect();
};

} // namespace

#endif // FSXSIMCONNECT_H
